﻿

namespace FolderSync
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Configuration;
    using System.Text.RegularExpressions;
    public class ConfigurationSet : ConfigurationSection
    {
        protected List<string> _argumentsProvided = new List<string>();
        protected static Configuration OpenConfig()
        {
            return ConfigurationManager.OpenExeConfiguration(System.Reflection.Assembly.GetAssembly(typeof(ConfigurationSet)).Location);
        }
        public ConfigurationSet()
            : base()
        {
            _argumentsProvided.Add("ALL");
        }
        public ConfigurationSet(string[] args)
            : base()
        {
            string key = string.Empty, value;
            foreach (var arg in args)
            {
                if (arg.StartsWith("-") || arg.StartsWith("/"))
                {
                    key = arg.Substring(1).ToLower();
                    value = string.Empty;
                }
                else value = arg;
                switch (key)
                {
                    case "src": _argumentsProvided.Add("Source"); this.Source = value; break;
                    case "dest": _argumentsProvided.Add("Destination"); this.Destination = value; break;
                    case "name": _argumentsProvided.Add("Name"); this.Name = value; break;
                    case "logfile": _argumentsProvided.Add("Logfile"); this.Logfile = value; break;
                    case "logtag": _argumentsProvided.Add("Logtag"); this.Logtag = value; break;
                    case "rd": _argumentsProvided.Add("rd"); this.IncludeDirectory = new Regex(value, RegexOptions.IgnoreCase); break;
                    case "rf": _argumentsProvided.Add("rf"); this.IncludeFile = new Regex(value, RegexOptions.IgnoreCase); break;
                    case "xd": _argumentsProvided.Add("xf"); this.ExcludeDirectory = new Regex(value, RegexOptions.IgnoreCase); break;
                    case "xf": _argumentsProvided.Add("xf"); this.ExcludeFile = new Regex(value, RegexOptions.IgnoreCase); break;
                    case "mir": _argumentsProvided.Add("Mirror"); this.Mirror = (value == string.Empty || value == "yes"); break;
                    case "merge": _argumentsProvided.Add("Merge"); this.Merge = (value == string.Empty || value == "yes"); break;
                    case "norun": _argumentsProvided.Add("NoRun"); this.NoRun = (value == string.Empty || value == "yes"); break;
                    default: break;
                }
            }
            if (!_argumentsProvided.Contains("Name")) _argumentsProvided.Add("Name"); this.Name = "CmdLine";
        }
        public void Save(string name)
        {
            try
            {
                this.Name =new System.Globalization.CultureInfo(string.Empty).TextInfo.ToTitleCase(name.ToLower());
                var cfgMgr = OpenConfig();
                var config = cfgMgr.GetCaseInsensitiveSection(string.IsNullOrWhiteSpace(name) ? "default" : name) as ConfigurationSet;
                if (config == null)
                {
                    cfgMgr.Sections.Add(this.Name, this);
                }
                else
                {
                    _myProps.All(p => { p.SetValue(config, p.GetValue(this, null), null); return true; });
                }
                cfgMgr.Save(ConfigurationSaveMode.Full);
            }
            catch (/*ConfigurationErrors*/Exception err)
            {
                Console.WriteLine(err.ToString());
            }
        }
        public static void Delete(string name)
        {
            if (string.IsNullOrWhiteSpace(name)) return;
            var cfgMgr = OpenConfig();
            var cfg = cfgMgr.GetCaseInsensitiveSection(name) as ConfigurationSet;
            if (cfg == null) return;
            cfgMgr.Sections.Remove(cfg.Name);
            cfgMgr.Save(ConfigurationSaveMode.Full);

        }
        public static ConfigurationSet GetConfigurationSet(string name)
        {
            var cfgMgr = OpenConfig();
            var config = cfgMgr.GetCaseInsensitiveSection(string.IsNullOrWhiteSpace(name) ? "default" : name) as ConfigurationSet;
            return config == null ? new ConfigurationSet() : config;
        }
        public static ConfigurationSet[] GetConfigurationSets(string name)
        {
            if (name.ToLower() == "all")
            {
                var configs = new List<ConfigurationSet>();
                var cfgMgr = OpenConfig();
                foreach (var sec in cfgMgr.Sections)
                {
                    var config = sec as ConfigurationSet;
                    if (config != null) configs.Add(config);
                }
                return configs.ToArray();
            }
            else return new ConfigurationSet[] { GetConfigurationSet(name) };
        }
        public static IEnumerable<string> Display(string name)
        {
            if (string.IsNullOrWhiteSpace(name) || name.ToLower() == "all") return DisplayAll();
            var cfgMgr = OpenConfig();
            var config = cfgMgr.GetCaseInsensitiveSection(name) as ConfigurationSet;
            if (config != null) return config.ToStrings();
            return new string[] { string.Format("No configuration section named «{0} » in config file.", name) };
        }
        public static string GetLogFileName(string name)
        {
            // if one exists for the given name, return it, else return default and if null return null;
            // if name specified is "all" return default and if null return null
            if (string.IsNullOrWhiteSpace(name) || name.ToLower() == "all")
            {
                var logFileName = GetLogFileName("Default");
                if (!string.IsNullOrWhiteSpace(logFileName)) return logFileName;
                else foreach (var cfg in GetConfigurationSets("all"))
                    {
                        if (!string.IsNullOrWhiteSpace(cfg.Logfile)) return cfg.Logfile;
                    }
                return null;
            }
            else
            {
                var cfgMgr = OpenConfig();
                var config = cfgMgr.GetCaseInsensitiveSection(name) as ConfigurationSet;
                if (config != null) return config.Logfile;
                else if (name != "Default") return GetLogFileName("default");
                else return null;
            }
        }
        public static IEnumerable<string> DisplayAll()
        {
            var cfgMgr = OpenConfig();
            foreach (var section in cfgMgr.Sections)
            {
                var config = section as ConfigurationSet;
                if (config != null)
                {
                    foreach (var line in config.ToStrings()) yield return line;
                }
            }
        }
        private IEnumerable<System.Reflection.PropertyInfo> _myProps = typeof(ConfigurationSet).GetProperties()
                .Where(prop => prop.DeclaringType == typeof(ConfigurationSet))
                .Where(prop => prop.PropertyType != typeof(Regex));

        public IEnumerable<string> ToStrings()
        {
            var maxLength = _myProps
                .Select(prop => prop.Name.Length)
                .Max();
            string format = string.Format("{{0,-{0}}} : {{1}}", maxLength);
            return new string[] { string.Format("{0} ´{1}´:", typeof(ConfigurationSet).Name, this.Name) }
                .Union(_myProps.Select(prop => string.Format(format, prop.Name, prop.GetValue(this, null))))
                .Union(new string[] { "--------------" });
        }
        public static ConfigurationSet operator +(ConfigurationSet left, ConfigurationSet right)
        {
            var ret = (ConfigurationSet)left.MemberwiseClone();
            var myProps = left.GetType().GetProperties()
                .Where(prop => prop.DeclaringType == left.GetType())
                .Where(prop => prop.PropertyType != typeof(Regex))
                .Where(prop => right._argumentsProvided.Contains(prop.Name))
                .All(prop =>
                {
                    prop.SetValue(ret, prop.GetValue(right, null), null);
                    return true;
                });
            return ret;
        }
        [ConfigurationProperty("name", IsRequired = true, IsKey = true, DefaultValue = "Unnamed")]
        public string Name
        {
            get { return (string)this["name"]; }
            set { this["name"] = value; }
        }
        [ConfigurationProperty("source", IsRequired = true, IsKey = false)]
        public string Source
        {
            get { return (string)this["source"]; }
            set { this["source"] = value; }
        }
        [ConfigurationProperty("destination", IsRequired = false, IsKey = false)]
        public string Destination
        {
            get { return (string)this["destination"]; }
            set { this["destination"] = value; }
        }
        [ConfigurationProperty("rd", IsRequired = false, IsKey = false, DefaultValue = null)]
        public string rd
        {
            get { return (string)this["rd"]; }
            set { this["rd"] = value; }
        }
        public Regex IncludeDirectory
        {
            get { return string.IsNullOrWhiteSpace(this["rd"].ToString()) ? null : new Regex(this["rd"].ToString(), RegexOptions.IgnoreCase); }
            set { this["rd"] = value.ToString(); }
        }
        [ConfigurationProperty("rf", IsRequired = false, IsKey = false, DefaultValue = null)]
        public string rf
        {
            get { return (string)this["rf"]; }
            set { this["rf"] = value; }
        }
        public Regex IncludeFile
        {
            get { return string.IsNullOrWhiteSpace(this["rf"].ToString()) ? null : new Regex(this["rf"].ToString(), RegexOptions.IgnoreCase); }
            set { this["rf"] = value.ToString(); }
        }
        [ConfigurationProperty("xd", IsRequired = false, IsKey = false, DefaultValue = null)]
        public string xd
        {
            get { return (string)this["xd"]; }
            set { this["xd"] = value; }
        }
        public Regex ExcludeDirectory
        {
            get { return string.IsNullOrWhiteSpace(this["xd"].ToString()) ? null : new Regex(this["xd"].ToString(), RegexOptions.IgnoreCase); }
            set { this["xd"] = value.ToString(); }
        }
        [ConfigurationProperty("xf", IsRequired = false, IsKey = false, DefaultValue = null)]
        public string xf
        {
            get { return (string)this["xf"]; }
            set { this["xf"] = value; }
        }
        public Regex ExcludeFile
        {
            get { return string.IsNullOrWhiteSpace(this["xf"].ToString()) ? null : new Regex(this["xf"].ToString(), RegexOptions.IgnoreCase); }
            set { this["xf"] = value.ToString(); }
        }
        [ConfigurationProperty("mir", IsRequired = false, IsKey = false, DefaultValue = false)]
        public bool Mirror
        {
            get { return (bool)this["mir"]; }
            set { this["mir"] = value; }
        }
        [ConfigurationProperty("merge", IsRequired = false, IsKey = false, DefaultValue = false)]
        public bool Merge
        {
            get { return (bool)this["merge"]; }
            set { this["merge"] = value; }
        }
        [ConfigurationProperty("norun", IsRequired = false, IsKey = false, DefaultValue = false)]
        public bool NoRun
        {
            get { return (bool)this["norun"]; }
            set { this["norun"] = value; }
        }
        [ConfigurationProperty("retry", IsRequired = false, IsKey = false, DefaultValue = 5)]
        public int Retry
        {
            get { return (int)this["retry"]; }
            set { this["retry"] = value; }
        }

        [ConfigurationProperty("logfile", IsRequired = false, IsKey = false, DefaultValue = null)]
        public string Logfile
        {
            get { return (string)this["logfile"]; }
            set { this["logfile"] = value; }
        }
        [ConfigurationProperty("logtag", IsRequired = false, IsKey = false, DefaultValue = "")]
        public string Logtag
        {
            get { return (string)this["logtag"]; }
            set { this["logtag"] = value; }
        }

        public static implicit operator ConfigurationSet[](ConfigurationSet cs)
        {
            return new ConfigurationSet[] { cs };
        }
    }
    public static class ConfigurationExtensions
    {
        public static object GetCaseInsensitiveSection(this Configuration cfgMgr, string name)
        {
            foreach (var sec in cfgMgr.Sections.Keys)
            {
                if (sec.ToString().ToLower() == name.ToLower())
                {
                    return cfgMgr.GetSection(sec.ToString());
                }
            }
            return null;
        }
    }
}
